package com.zyt.wiki.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.zyt.wiki.resp.CommonResp;
import com.zyt.wiki.util.SnowFlake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * 【非法请求拦截器】 拦截非法请求，比如XSS跨站脚本攻击
 *  注：非法请求的过滤，其实应该放在过滤器中更合适些，
 *     本项目放这里是因为现有的Spring提供的跨域解决方法CorsRegistry，不能对Filter起作用，还得用其他方法解决本地测试跨越问题，比如用nginx
 */
@Component
public class IllegalRequestInterceptor implements HandlerInterceptor {

    public static final Logger LOG = LoggerFactory.getLogger(IllegalRequestInterceptor.class) ;

    @Resource
    private SnowFlake snowFlake ;

    // 可以优化成从Nacos读取，能够动态刷新
    private static final String REQ_DENY_PATTERN[] = {
            "<script", "javascript", "<img", "<a", "<div", "<input", "<bgsound", "<link", "<iframe", "<meta",
            "<body", "<style", "<object", "<xml", "<applet", "<embed", "<frameset", "<frame", "<layer", "<ilayer",
            "<svg", "style=", "xss:", "../", "alert("
    };

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 增加日志流水号 (并发场景下用于日志跟踪)
        MDC.put("LOG_ID", String.valueOf(snowFlake.nextId()));

        // OPTIONS请求不做校验（前后端分离的架构, 前端会发一个OPTIONS请求先做预检, 对预检请求不做校验）
        if(request.getMethod().toUpperCase().equals("OPTIONS")){
            return true ; // 返回true，请求会继续往后；返回false，则请求中断
        }
        // 获取请求体中的参数（不直接使用requst获取输入流，因为流只能读取一次，会影响控制器中获取数据）
        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
        StringBuilder sb = new StringBuilder() ;
        String line = null ;
        while ((line = reader.readLine()) != null){
            sb.append(line) ;
        }
        String paramString = sb.toString() ;
        // 若报文体不为空，且包含非法字符串，则直接拦截 （这里不能通过抛出异常给统一异常处理，因为还没到Controller层）
        if(null != paramString && this.isIllegalString(paramString)){
            LOG.error("request param is legal!, the param string: " + paramString) ;
//            response.setStatus(HttpStatus.BAD_REQUEST.value()) ; // 为方便前端VUE获取报文信息进行弹框提示，这里就先返回200，不返回400了
            response.setStatus(HttpStatus.OK.value()) ;
            response.setContentType("application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            CommonResp<String> commonResp = new CommonResp<>() ;
            commonResp.setSuccess(false) ;
            commonResp.setMessage("非法请求参数！");
            response.getWriter().print(JSONObject.toJSON(commonResp));
            return false ; // 返回false，则请求中断，不再往后到 其他拦截器、Controller
        }
        return true ;
    }

    /**
     * 判断字符串是否非法
     * @param str   输入字符串
     * @return  true--非法字符串
     */
    private boolean isIllegalString(String str) {
        for (int i = 0; i < REQ_DENY_PATTERN.length; i++) {
            if(str.toLowerCase().contains(REQ_DENY_PATTERN[i])){
                return true ;
            }
        }
        return false ;
    }

}
